#!/bin/python
"""Word based functions that query a dictionary database"""

#TODO:
#non-english dictionary
#anagrams with blanks
#plain list mode
#scrabble mode
#scrabble score

from itertools import permutations
from string import ascii_lowercase
import math
import os
import sys
import apsw

try:
	DICT = os.environ['DICT']
except KeyError:
	DICT = "shed-dict"
DATA = apsw.Connection(DICT)
CURSOR = DATA.cursor()
COL = {'PURPLE': '\033[95m', 'CYAN': '\033[96m', 'BLUE': '\033[94m',
       'GREEN': '\033[92m', 'YELLOW': '\033[93m', 'RED': '\033[91m',
       'BOLD': '\033[1m', 'UNDERLINE': '\033[4m', 'END': '\033[0m'}
COMMANDS = ['define', 'crossword', 'anagram']
NOARGS = ['stats', 'dump', 'help']
COMMANDS = COMMANDS + NOARGS

def valid(wordarray):
	"""Returns list of valid words from a list of words"""
	words = str(wordarray).replace("[", "(").replace("]", ")")
	command = "select distinct word from english where word in " + words + " order by word"
	found = list(CURSOR.execute(command))
	result = [word[0] for word in found]
	return result

def display(words):
	"""Displays long lists of words in a neat table"""
	res = len(words)
	if res == 0:
		return
	maxwidth = int(os.popen('stty size', 'r').read().split()[1])
	width = max(len(word) for word in words) + 1
	#rows = min(20, res)
	rows = math.ceil(res * width / (maxwidth - width))
	cols = math.ceil(res / rows)
	for row in range(0, rows):
		if row == res - (cols - 1) * rows:
			#res % rows and res != rows:
			cols -= 1
		for col in range(0, cols):
			print(words[row + col * rows].ljust(width), end='')
		print()

def crossword(letters):
	"""Solves a crossword-style problem by filling in missing letters"""
	print("Solutions to " + COL['BOLD'] + letters + COL['END'] + ":")
	results = valid(getpossiblewords(letters))
	if not results:
		print(COL['RED'] + "None." + COL['END'])
	display(results)

def getpossiblewords(letters):
	"""Generate possible permutations which satisfy a crossword-style clue"""
	wordlist = []
	i = 0
	for char in letters:
		if char == "?":
			for letter in ascii_lowercase:
				if len(letters) == 1:
					newletters = letter
				elif i == 0:
					newletters = letter + letters[1 - len(letters):]
				elif i == len(letters) - 1:
					newletters = letters[:i] + letter
				else:
					newletters = letters[:i] + letter + letters[i + 1 - len(letters):]
				wordlist = wordlist + getpossiblewords(newletters)
			return wordlist
		elif not char.isalpha():
			raise SystemExit("Invalid character")
		i += 1
	wordlist.append(letters)
	return wordlist

def anagram(letters):
	"""Lists valid anagrams of given list of letters"""
	print("Anagrams of " + COL['BOLD'] + letters + COL['END'] + ":")
	if len(letters) == 1 and valid([letters]):
		print(letters)
	elif len(letters) > 1:
		perms = []
		for i in range(2, len(letters) + 1):
			for ana in permutations(letters, i):
				perms.append(''.join(ana))
		valids = valid(perms)
		valids.sort(key=len)
		if not valids:
			print(COL['RED'] + "None." + COL['END'])
		display(valids)

def define(word):
	"""Fetch definitions of given word"""
	correct = valid([word])
	if correct:
		print(COL['GREEN'], end='')
	else:
		print(COL['RED'], end='')
	print(COL['BOLD'] + word + COL['END'])
	if not correct:
		print("Word not found.")
	oldcat = ""
	command = "select category,definition from english where word = \"" + word + "\" order by category"
	for row in CURSOR.execute(command):
		if row[0] != oldcat:
			i = 0
			print(COL['BOLD'] + row[0] + COL['END'])
		i += 1
		print(str(i) + ". " + row[1])
		oldcat = row[0]

def stats():
	"""Show statistics about database"""
	defs = str(list(CURSOR.execute("select count(*) from english;"))[0][0])
	words = str(list(CURSOR.execute("select count(distinct word) from english;"))[0][0])
	size = str(round(os.stat(DICT).st_size / (1024 ** 2), 2))
	print(defs + " total definitions, " + words + " unique words.")
	print("Database size: " + size + "MB")

def dump():
	"""Outputs complete list of all words in database"""
	output = list(CURSOR.execute("select distinct word from english order by word"))
	words = [word[0] for word in output]
	for word in words:
		print(word)

def help():
	"""Help text explaining how to use the program"""
	print("Shell Dictionary - by Oliver Galvin")
	print("Command line based tool to define words, solve anagrams and crosswords")
	print("")
	print("Commands:")
	print("define WORD -		show definitions of WORD, if it is a valid word")
	print("crossword LETTERS -	solve crossword clue made up of the given letters, with a ? for blanks")
	print("anagram LETTERS -	list words that are anagrams of the given letters")
	print("stats -			show statistics about the dictionary database")
	print("dump -			output a list of all words in the database")
	print("help -			show this help text")
	print("")

if len(sys.argv) == 1:
	raise SystemExit("Missing command.")
elif sys.argv[1] in COMMANDS:
	if sys.argv[1] in NOARGS:
		if len(sys.argv) == 2:
			globals()[sys.argv[1]]()
		else:
			raise SystemExit("Too many arguments.")
	elif len(sys.argv) == 2:
		raise SystemExit("Missing argument.")
	else:
		globals()[sys.argv[1]](sys.argv[2])
else:
	raise SystemExit("Invalid command.")

DATA.close(True)
